package dao

import (
	"context"
	"time"

	"github.com/ego-component/egorm"
	"github.com/go-sql-driver/mysql"
	"github.com/pkg/errors"
)

type ChannelMemberDAO interface {
	Insert(ctx context.Context, cm ChannelMember) error
	DeleteByCIDAndUID(ctx context.Context, cid, uid int64) error
	DeleteByCID(ctx context.Context, cid int64) error
	FindMembersByCID(ctx context.Context, cid int64) ([]ChannelMember, error)
	FindAllChannelMembersByUID(ctx context.Context, uid int64) ([]ChannelMember, error)
	FindByCIDAndUID(ctx context.Context, cid, uid int64) (ChannelMember, error)
	UpdateByCIDAndUID(ctx context.Context, cm ChannelMember) (ChannelMember, error)
}

type gormChannelMemberDAO struct {
	db *egorm.Component
}

func NewGORMChannelMemberDAO(db *egorm.Component) ChannelMemberDAO {
	return &gormChannelMemberDAO{
		db: db,
	}
}

func (dao *gormChannelMemberDAO) Insert(ctx context.Context, cm ChannelMember) error {
	now := time.Now().UnixMilli()
	cm.Ctime = now
	err := dao.db.WithContext(ctx).Create(&cm).Error
	me := new(mysql.MySQLError)
	if ok := errors.As(err, &me); ok {
		const uniqueConflicts uint16 = 1062
		if me.Number == uniqueConflicts {
			// ChannelID 和Uid有复合唯一索引
			return ErrDuplicateRecord
		}
	}
	return err
}

func (dao *gormChannelMemberDAO) DeleteByCIDAndUID(ctx context.Context, cid, uid int64) error {
	cm := ChannelMember{
		ChannelID: cid,
		UID:       uid,
	}
	res := dao.db.WithContext(ctx).
		Where("channel_id = ? AND uid = ?", cm.ChannelID, cm.UID).
		Delete(&cm)
	return res.Error
}

func (dao *gormChannelMemberDAO) DeleteByCID(ctx context.Context, cid int64) error {
	cm := ChannelMember{
		ChannelID: cid,
	}
	res := dao.db.WithContext(ctx).
		Where("channel_id = ?", cm.ChannelID).
		Delete(&cm)
	return res.Error
}

func (dao *gormChannelMemberDAO) FindMembersByCID(ctx context.Context, cid int64) ([]ChannelMember, error) {
	var cms []ChannelMember
	err := dao.db.WithContext(ctx).Where("channel_id = ?", cid).
		Order("join_time ASC").
		Find(&cms).Error
	return cms, err
}

func (dao *gormChannelMemberDAO) FindAllChannelMembersByUID(ctx context.Context, uid int64) ([]ChannelMember, error) {
	var cms []ChannelMember
	err := dao.db.WithContext(ctx).Where("uid = ?", uid).
		Order("ctime DESC").
		Find(&cms).Error
	return cms, err
}

func (dao *gormChannelMemberDAO) FindByCIDAndUID(ctx context.Context, cid, uid int64) (ChannelMember, error) {
	var cm ChannelMember
	err := dao.db.WithContext(ctx).Where("channel_id = ? AND uid = ?", cid, uid).
		First(&cm).Error
	return cm, err
}

func (dao *gormChannelMemberDAO) UpdateByCIDAndUID(ctx context.Context, cm ChannelMember) (ChannelMember, error) {
	cm.Utime = time.Now().UnixMilli()
	res := dao.db.WithContext(ctx).Model(&cm).
		Where(map[string]any{"channel_id": cm.ChannelID, "uid": cm.UID}).
		Updates(&cm)
	if res.RowsAffected == 0 {
		return ChannelMember{}, ErrChannelMemberRecordNotFound
	}
	return cm, res.Error
}

type ChannelMember struct {
	ID                int64  `gorm:"primaryKey,autoIncrement"`
	ChannelID         int64  `gorm:"not null;index;uniqueIndex:channel_id_uid"`
	UID               int64  `gorm:"not null;index;uniqueIndex:channel_id_uid"`
	Note              string `gorm:"type:varchar(256);default:'';comment:用户对群聊的备注"`
	NicknameInChannel string `gorm:"type:varchar(256);default:'';comment:用户在群聊中的昵称"`
	Priority          int    `gorm:"not null;default:0;comment:群聊优先级,0未置顶,1置顶"`
	Mute              bool   `gorm:"not null;default:0;comment:群聊是否免打扰,0未免打扰,1免打扰"`
	JoinTime          int64  `gorm:"not null;default:0;comment:加入时间UTC毫秒数"`
	Ctime             int64  `gorm:"not null;default:0;comment:创建时间UTC毫秒数"`
	Utime             int64  `gorm:"not null;default:0;comment:更新时间UTC毫秒数"`
}
